package com.lin.demo.service.task;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * @Author zimingl
 * @Date 2023/4/2 22:37
 * @Description: AsyncTask
 *
 * 使用@Async注解可能会导致的问题
 * 如果serviceA、serviceB对象之间相互依赖，serviceA和serviceB总一个一个会先实例化，而serviceA或serviceB里面使用了@Async注解，会导致循环依赖异常：org.springframework.beans.factory.BeanCurrentlyInCreationException
 *
 * 在springboot中，以上报错被捕捉，抛出的异常是：
 * The dependencies of some of the beans in the application context form a cycle
 *
 * 原因
 * 我们知道，spring三级缓存一定程度上解决了循环依赖问题。A对象在实例化之后，属性赋值【opulateBean(beanName, mbd, instanceWrapper)】执行之前，将ObjectFactory添加至三级缓存中，从而使得在B对象实例化后的属性赋值过程中，能从三级缓存拿到ObjectFactory，调用getObject()方法拿到A的引用，B由此能顺利完成初始化并加入到IOC容器。此时A对象完成属性赋值之后，将会执行初始化【initializeBean(beanName, exposedObject, mbd)方法】，重点是@Async注解的处理正是在这地方完成的，其对应的后置处理器AsyncAnnotationBeanPostProcessor，在postProcessAfterInitialization方法中将返回代理对象，此代理对象与B中持有的A对象引用不同，导致了以上报错。
 *
 * 解决办法
 * 1.在A类上加@Lazy，保证A对象实例化晚于B对象
 * 2.不使用@Async注解，通过自定义异步工具类发起异步线程（线程池）
 * 3.不要让@Async的Bean参与循环依赖
 */

@Slf4j
@Component
public class AsyncTask {
    @SneakyThrows
//    @Async  // 在异步配置类设置了默认线程池则不需要再指定线程池名称
//    @Async("asyncPoolTaskExecutor")
    public void doTask1() {
        long t1 = System.currentTimeMillis();
        Thread.sleep(60000);
        long t2 = System.currentTimeMillis();
        log.info("task1方法耗时 {} ms" , t2-t1);
    }

    @SneakyThrows
//    @Async
//    @Async("otherPoolTaskExecutor") // 其他线程池的名称
    public void doTask2() {
        long t1 = System.currentTimeMillis();
        Thread.sleep(60000);
        long t2 = System.currentTimeMillis();
        log.info("task2方法耗时 {} ms" , t2-t1);
    }
}
